<?php
/** Yz_SQL_DMLStatement
*
*/

abstract class Yz_SQL_DMLStatement
    extends Yz_SQL_Statement
    implements Yz_SQL_Statement_Interface
{

    const CLAUSE_WHERE = 'WHERE';
    const CLAUSE_ORDER_BY = 'ORDER BY';
    const CLAUSE_LIMIT = 'LIMIT';
    const LOGICAL_OR = 'OR';
    const LOGICAL_AND = 'AND';

    // join types
    const COMMA_JOIN = ",";
    const INNER_JOIN = "INNER JOIN";
    const LEFT_JOIN = "LEFT JOIN";
    const JOIN_ON = 'ON';
    const JOIN_USING = 'USING';

    /** used for _table()
     *
     */
    const CLAUSE_TABLES = '_tables_';

    /**
     *  @var array
     */
    protected static $joinTypes = array(
        self::COMMA_JOIN => ",\n  ",
        self::INNER_JOIN => "\n  INNER JOIN ",
        self::LEFT_JOIN => "\n  LEFT JOIN ",
    );



    /** getSQL() : get the string representation of the current statement
    *
    *
    */
    public function getSQL()
    {
        return $this->assemble();
    } // getSQL()


    /** TODO must be a possibility to execute query as prepared stmt
    *
    *   @return <this>
    */
    public function execute()
    {
        debug();
        $this->sql = $this->assemble();

        return parent::execute();
    } // execute


    /** Add a table to the TABLES clause
     *      If not in JOIN, also set the TABLES clause name
     */
    protected function _table( $clause, $table, $alias )
    {
        $this->sql = null;
        $table = $this->_quoteIdentifier($table);
        if (null !== $alias)
            $table .= ' ' . $this->_quoteIdentifier($alias);

        $this->clauses[$clause] = $clause;
        $this->clauses[self::CLAUSE_TABLES] = '  ' . $table;

        return $this;
    }


    /**
     *  @param args
     */
    protected function _joinTable($type, $table, $alias, $how, $conds)
    {
        debug();
        $types = self::$joinTypes;
        if (null === $type)
            $join = $types[self::COMMA_JOIN];
        else
            if (! isset($types[$type]))
                throw new Yz_SQL_Exception("Unknown join type");
            else
                $join = $types[$type];

        $this->sql = null;
        $join .= $this->_quoteIdentifier($table);
        if (null !== $alias)
            $join .= ' ' . $this->_quoteIdentifier($alias);

        if ( null !== $type and self::COMMA_JOIN !== $type ) {
            if (self::JOIN_ON !== $how and self::JOIN_USING !== $how)
                throw new Yz_SQL_Exception("Unknown join conditions type");
            $join .= " {$how} ($conds)";
        }

        $this->clauses[self::CLAUSE_TABLES] .= $join;
        return $this;
    }

    /**
     *  empty $what removes clause
     */
    protected function _where( $clause, $what, $logicalOp='AND' )
    {
        $this->sql = null;
        if ("" !== "$what") {
            $c = $this->clauses[$clause];
            if ("" === $c)
                $c = $clause;
            else
                $c .= "\n  {$logicalOp}\n  ";

            $what = $c . "\n  " . $what;
        }
        $this->clauses[$clause] = $what;

        return $this;
    }

    /**
     *  empty $what removes clause
     */
    protected function _orderBy( $clause, $what )
    {
        $this->sql = null;
        if ("" !== "$what") {
            $c = $this->clauses[$clause];
            if ("" === $c)
                $c = $clause;
            else
                $c .= ",\n  ";

            $what = $c . "\n  " . $what;
        }
        $this->clauses[$clause] = $what;

        return $this;
    }


    /**
     *  @param int $offset >= 0
     *  @param int $limit  >= 0, 0 means no limit, remove limit clause entirely
     *
     */
    protected function _limit( $offset, $limit )
    {
        debug();

        // if the limit is 0, limiting is effectively disabled
        if ( $limit > 0 ) {
            $clause = $limit;

            // include offset in the clause only if it is > 0
            if ($offset > 0)
                $clause = $offset . "," . $clause;

            $clause = self::CLAUSE_LIMIT . ' ' . $clause;
        } else {
            $clause = "";
        }
        $this->sql = null;
        $this->clauses[self::CLAUSE_LIMIT] = $clause;
        return $this;
    } // limit


    /** assemble() : assemble sql statement
     *     and in some statement classes, substitute parameters
     *
     *  @return string SQL
     */
    protected function assemble()
    {
        debug();

        // cached statement
        if( null === $this->sql ) {
            if ( ! $this->isComplete() )
                throw new Yz_Exception( "The statement is not complete" );

            $sql = join( "\n", array_filter(
                $this->clauses,
                function($a){return ""!=="$a";}
            ));
        } else {
            $sql = $this->sql;
        }

        return $sql;
    }


    /** testComplete() : is sql statement complete?
     *
     *  @return bool
     */
    protected function isComplete()
    {
        return false;
    }


} // class

